package redis

import (
	"errors"
	"math"
	"strconv"
	"time"

	"github.com/go-redis/redis"
	"go.uber.org/zap"
)

const (
	oneWeekInSec = 7 * 24 * 3600
	scorePerVote = 432
)

var (
	ErrVoteTimeExpired = errors.New("超出投票时间")
	ErrVoteRepeated    = errors.New("不允许重复投票")
)

func CreatePost(pid, cid int64) error {
	pipeline := client.TxPipeline()
	pipeline.ZAdd(getRedisKey(KeyPostTimeZSet), redis.Z{
		Score:  float64(time.Now().Unix()),
		Member: pid})

	pipeline.ZAdd(getRedisKey(KeyPostScoreZSet), redis.Z{
		Score:  float64(time.Now().Unix()),
		Member: pid})

	cKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(cid)))
	pipeline.SAdd(cKey, pid)

	_, err := pipeline.Exec()
	if err != nil {
		zap.L().Error("redis创建帖子失败", zap.Error(err))
	}
	return err
}

/* 帖子投票
   用户投一票，就加432分
   direction=1时，两种情况：
   1、用户之前没有投过票，现在投赞成票  + 432分
   2、用户之前投反对票，现在变成赞成票  + 432分 * 2
   direction=0时，两种情况：
   1、用户之前投反对票，现在变成取消投票 + 432分
   2、用户之前投赞成票，现在变成取消投票 - 432分
   direction=-1时，两种情况：
   1、用户之前没有投过票，现在变成投反对票 - 432分
   2、用户之前投赞成票，现在变成投反对票 - 432分 * 2

   投票的限制：
   每个帖子超过一个星期不允许再投票
   1、到期之后把redis中保存的票数存储到mysql表中
   2、到期之后把KeyPostVotedZsetPrefix删除
*/

func PostVote(userID, postID string, v float64) error {
	//1、判断投票限制
	zap.L().Debug("redis 帖子投票", zap.String("userID", userID), zap.String("postID", postID), zap.Float64("v", v))
	postTime := client.ZScore(getRedisKey(KeyPostTimeZSet), postID).Val()
	zap.L().Debug("postTime", zap.Float64("postTime", postTime))
	if float64(time.Now().Unix())-postTime > oneWeekInSec {
		return ErrVoteTimeExpired
	}
	// 2、更新分数

	// 先查当前用户给当前帖子的投票纪录
	ov := client.ZScore(getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()
	if v == ov {
		return ErrVoteRepeated
	}
	var op float64
	if v > ov {
		op = 1
	} else {
		op = -1
	}

	diff := math.Abs(ov - v) // 计算两次投票的差值

	pipeline := client.TxPipeline()
	pipeline.ZIncrBy(getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)
	// 3、记录用户为该帖子投票
	if v == 0 {
		pipeline.ZRem(getRedisKey(KeyPostVotedZSetPF+postID), postID)
	} else {
		pipeline.ZAdd(getRedisKey(KeyPostVotedZSetPF+postID), redis.Z{
			Score:  v, // 赞成票还是反对票
			Member: userID,
		})
	}
	_, err := pipeline.Exec()
	if err != nil {
		zap.L().Error("redis 帖子投票失败", zap.Error(err))
	}
	return err
}
